package raft

import (
	"context"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"time"
)

func (n *RaftNode) RequestVote(ctx context.Context, req *RequestVoteReq) (*RequestVoteResp, error) {
	n.mu.Lock()
	defer n.mu.Unlock()

	resp := &RequestVoteResp{
		Term:        n.currentTerm,
		VoteGranted: false,
	}

	if req.Term < n.currentTerm {
		return resp, nil
	}

	if req.Term > n.currentTerm {
		n.currentTerm = req.Term
		n.state = Follower
		n.votedFor = -1
		n.persister.SaveState(n.currentTerm, n.votedFor, n.logs, n.lastSnapshotIndex, n.lastSnapshotTerm)
	}

	lastLogIndex := n.lastLogIndex()
	lastLogTerm := n.lastLogTerm()

	// 检查日志是否是最新的
	upToDate := req.LastLogTerm > lastLogTerm ||
		(req.LastLogTerm == lastLogTerm && req.LastLogIndex >= lastLogIndex)

	if (n.votedFor == -1 || n.votedFor == req.CandidateId) && upToDate {
		n.votedFor = req.CandidateId
		resp.VoteGranted = true
		n.lastHeard = time.Now()
		n.persister.SaveState(n.currentTerm, n.votedFor, n.logs, n.lastSnapshotIndex, n.lastSnapshotTerm)
	}

	return resp, nil
}

func (n *RaftNode) AppendEntries(ctx context.Context, req *AppendEntriesReq) (*AppendEntriesResp, error) {
	n.mu.Lock()
	defer n.mu.Unlock()

	resp := &AppendEntriesResp{
		Term:    n.currentTerm,
		Success: false,
	}

	if req.Term < n.currentTerm {
		return resp, nil
	}

	n.lastHeard = time.Now()

	if req.Term > n.currentTerm || n.state == Candidate {
		n.currentTerm = req.Term
		n.state = Follower
		n.votedFor = -1
	}

	// 检查前一日志
	if req.PrevLogIndex > n.lastLogIndex() ||
		(req.PrevLogIndex > n.lastSnapshotIndex && n.logTerm(req.PrevLogIndex) != req.PrevLogTerm) {
		resp.ConflictIndex = n.lastLogIndex() + 1
		return resp, nil
	}

	// 追加新日志
	if len(req.Entries) > 0 {
		newStart := req.PrevLogIndex + 1
		for i, entry := range req.Entries {
			idx := newStart + int64(i)
			if idx <= n.lastLogIndex() && n.logTerm(idx) != entry.Term {
				// 截断冲突日志
				n.logs = n.logs[:idx-n.lastSnapshotIndex]
				n.logs = append(n.logs, entry)
			} else if idx > n.lastLogIndex() {
				n.logs = append(n.logs, entry)
			}
		}
	}

	// 更新提交索引
	if req.LeaderCommit > n.commitIndex {
		n.commitIndex = min(req.LeaderCommit, req.PrevLogIndex+int64(len(req.Entries)))
	}

	resp.Success = true
	n.persister.SaveState(n.currentTerm, n.votedFor, n.logs, n.lastSnapshotIndex, n.lastSnapshotTerm)

	return resp, nil
}

func (n *RaftNode) ExecuteCommand(ctx context.Context, req *CommandReq) (*CommandResp, error) {
	n.mu.Lock()

	if n.state != Leader {
		leaderID := int32(0)
		if len(n.peers) > 0 {
			// 尝试寻找已知Leader
			for id := range n.peers {
				leaderID = id
				break
			}
		}
		n.mu.Unlock()
		return &CommandResp{
			Success:  false,
			LeaderId: leaderID,
		}, nil
	}

	// 添加到日志
	entry := &LogEntry{
		Term:    n.currentTerm,
		Command: req.Command,
		Index:   n.lastLogIndex() + 1,
	}

	n.logs = append(n.logs, entry)
	idx := entry.Index

	// 保存状态
	n.persister.SaveState(n.currentTerm, n.votedFor, n.logs, n.lastSnapshotIndex, n.lastSnapshotTerm)
	n.mu.Unlock()

	// 等待提交
	timeout := time.After(2 * time.Second)

	for {
		select {
		case <-timeout:
			return nil, status.Error(codes.DeadlineExceeded, "command commit timeout")
		default:
			n.mu.Lock()
			committed := n.commitIndex >= idx
			n.mu.Unlock()

			if committed {
				// 状态机将应用该日志并在applier中返回结果
				return &CommandResp{Success: true}, nil
			}
			time.Sleep(10 * time.Millisecond)
		}
	}
}
